#include "Database.h"
#include "io.h"
#include "Config.h"
#include "FeatureFunctions.h"
#include <utility>
#include <iostream>
#include <queue>

using namespace std;

Database::Database()
{
  initialize();
}

Database::Database(string filename)
{
  initialize();
  addDatabaseFile(filename);
}

Database::~Database()
{
}

void Database::initialize()
{
  d_prototypes = vector< pair<string, Descriptors> >();
}

class MatchCompare
{
  public:
  bool operator() (const Match firstMatch, const Match secondMatch) const {
    return firstMatch.score > secondMatch.score;
  }
};

vector<Match> Database::getBestMatches(Descriptors features){  
  vector<Match> result;
  if (d_prototypes.size() == 0)
  {
    cout << "ERROR! comparing against empty database!!!" << endl;
    Match match; 
    match.text = "Error empty database"; 
    match.score = 0;
    result.push_back(match);
    return result;
  }
  
  //Match word against all the prototypes in the database 
  priority_queue<Match, vector<Match>, MatchCompare> matches;
  for(unsigned index = 0; index < d_prototypes.size(); index++)
  {
    Match match;
    match.text = d_prototypes.at(index).first;
    Descriptors matchFeatures = d_prototypes.at(index).second;
    
    double wordLengthDistance = abs(double(matchFeatures.wordLength - features.wordLength))/matchFeatures.wordLength * 100;
    
    //if the word length differs to much, the prototype is not a candidate
    if(Config::getInstance()->maxWordLengthDifference < wordLengthDistance)continue;
    
    double radonDistance = 0.0;
    double bottomProfileDistance = 0.0;
    double lowResolutionDistance = 0.0;
    if(0 < Config::getInstance()->radonDistanceWeight){
      radonDistance = featureDistance(matchFeatures.radonDescriptor,features.radonDescriptor);
    }
    if(0 < Config::getInstance()->bottomProfileDistanceWeight){
      bottomProfileDistance = getBottomProfileDistance(matchFeatures.bottomProfileDescriptor, features.bottomProfileDescriptor);
    }
    if(0 < Config::getInstance()->lowResolutionWordDistanceWeight){
      lowResolutionDistance = getLowResolutionDistance(matchFeatures.lowResolution, features.lowResolution);
    }
    double heightWidthRatioDistance = getHeightWidthRatioDistance(matchFeatures.heightWidthRatio, features.heightWidthRatio);
   
    if(Config::getInstance()->applyWeightsBeforeSquaring){
      match.score = radonDistance
	+ wordLengthDistance
	+ bottomProfileDistance
	+ heightWidthRatioDistance
	+ lowResolutionDistance;
    }else{
      match.score = (Config::getInstance()->radonDistanceWeight * radonDistance) 
	+ (Config::getInstance()->wordLengthDifferenceWeight * wordLengthDistance) 
	+ (Config::getInstance()->bottomProfileDistanceWeight * bottomProfileDistance)
	+ (Config::getInstance()->heightWidthRatioWeight * heightWidthRatioDistance)
	+ (Config::getInstance()->lowResolutionWordDistanceWeight * lowResolutionDistance);
    }
    matches.push(match);
  }
  
  //get the 5 best matches
  for(int i = 0; i < 5 && 0 < matches.size(); i++){
    result.push_back(matches.top());
    matches.pop();
  }
  
  return result;
}




void Database::addPrototype(string target, Descriptors features)
{
  pair <string, Descriptors> entry;
  entry.first  = target;
  entry.second = features;
  d_prototypes.push_back(entry);
}


void Database::saveDatabase(string filename)
{
  writeDatabase(d_prototypes, filename);
}


void Database::addDatabaseFile(string filename)
{
  vector< pair<string, Descriptors> > newprototypes = readDatabase(filename);
  for(unsigned index = 0; index < newprototypes.size(); index++)
    addPrototype(newprototypes.at(index).first, newprototypes.at(index).second);
}

void Database::printData()
{
  for(unsigned index = 0; index < d_prototypes.size(); index++)
    cout << d_prototypes.at(index).first << endl;
}






















